package com.rslai.base.tool.servertest.objectfactory;

import java.lang.reflect.Type;
import java.sql.Time;
import java.sql.Timestamp;
import java.text.DateFormat;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.Calendar;

import org.apache.commons.lang.StringUtils;

public class DateTimeFactory extends InstanceFactory {
    private static final String[] FORMATS = {"yyyy-MM-dd hh:mm:ss", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd", "yyyy-MM-dd HH:mm"};

    protected Object create(Type type, Object value) {
        if (value == null) {
            return null;
        }
        String valueStr = value.toString();
        if (StringUtils.isBlank(valueStr)) {
            return null;
        }
        if (StringUtils.isNumeric(valueStr)) {
            return toDate(type, Long.valueOf(valueStr));
        }
        return toDate(type, valueStr);
    }

    private Object toDate(Type type, Long timestamp) {
        if (type.equals(java.sql.Date.class)) {
            return new java.sql.Date(timestamp.longValue());
        }
        if (type.equals(Timestamp.class)) {
            return new Timestamp(timestamp.longValue());
        }
        if (type.equals(java.util.Date.class)) {
            return new java.util.Date(timestamp.longValue());
        }
        if (type.equals(Time.class)) {
            return new Time(timestamp.longValue());
        }
        if (type.equals(Calendar.class)) {
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(new java.util.Date(timestamp.longValue()));
            calendar.setLenient(false);
            return calendar;
        }
        throw new RuntimeException("Can not cast long to given type:" + type);
    }

    private Object toDate(Type type, String timeStr) {
        if (type.equals(java.sql.Date.class)) {
            return java.sql.Date.valueOf(timeStr);
        }
        if (type.equals(Timestamp.class)) {
            return Timestamp.valueOf(timeStr);
        }
        if (type.equals(Time.class)) {
            return Time.valueOf(timeStr);
        }
        Calendar calendar = parse(timeStr);
        if (Calendar.class.isAssignableFrom((Class) type)) {
            return calendar;
        }
        return toDate(type, Long.valueOf(calendar.getTime().getTime()));
    }

    private Calendar parse(String timeStr) {
        RuntimeException lastException = null;
        for (int i = 0; i < FORMATS.length; i++) {
            try {
                return parse(timeStr, getFormat(FORMATS[i]));
            } catch (RuntimeException e) {
                lastException = e;
            }
        }
        throw lastException;
    }

    private DateFormat getFormat(String format) {
        return new SimpleDateFormat(format);
    }

    private Calendar parse(String value, DateFormat format) {
        format.setLenient(false);
        ParsePosition pos = new ParsePosition(0);
        java.util.Date parsedDate = format.parse(value, pos);
        if ((pos.getErrorIndex() >= 0) || (pos.getIndex() != value.length()) || (parsedDate == null)) {
            throw new RuntimeException("Can not format " + value + " to:" + format);
        }
        return format.getCalendar();
    }

    protected boolean support(Type type) {
        return (type.equals(java.util.Date.class)) || (type.equals(java.sql.Date.class)) || (type.equals(Time.class)) || (type.equals(Timestamp.class)) || (type.equals(Calendar.class));
    }
}
